package cz.cuni.mff.ufal.volk.services;

import java.util.List;

import cz.cuni.mff.ufal.volk.Expression;
import cz.cuni.mff.ufal.volk.UnsupportedInputTypeException;

/**
 * <p>An extended language recognizer. It extends the {@link LanguageRecognizer} interface
 * and provides the additional method {@link #recognize(Expression, int)} which returns
 * not only the best hypothesis but the list of several hypotheses with their rating.</p>
 * <p>The method {@link LanguageRecognizer#recognize(Expression)} should be equivalent
 * to calling the method {@link #recognize(Expression, int)} with the same first parameter
 * and taking the language of the first hypothesis returned.</p>
 *
 * @author Bartłomiej Etenkowski
 *
 * @param <T> the input type
 */
public interface LanguageRecognizerExtended<T extends Expression>
extends LanguageRecognizer<T> {
  /**
   * Tries to recognize the language of the given item. The method returns the list
   * of the hypotheses on the language of the given item. The method should never return
   * {@code null}. If no hypothesis is found, an empty {@link List} should be returned.
   *
   * @param  item the item to recognize
   * @param  max if greater than 0 then the maximal count of the hypotheses in the result list.
   *         Otherwise the indicator that no limit should be used.
   * @return the list of the hypotheses ordered from the best (with the highest rating)
   *         to the worst
   */
  List<Hypothesis> recognize(T item, int max);

  /**
   * A language recognition hypothesis.
   *
   * @author Bartłomiej Etenkowski
   */
  public static class Hypothesis implements Comparable<Hypothesis> {
    /**
     * Creates a Hypothesis.
     *
     * @param  language the recognized language
     * @param  rating the rating of the hypothesis. It should be between 0 and 1.
     */
    public Hypothesis(String language, double rating) {
      language.getClass(); // null check
      this.language = language;
      this.rating = rating;
    }

    /** The recognized language. */
    public final String language;

    /** The rating of the hypothesis (a real number between 0 and 1). */
    public final double rating;

    @Override
    public int compareTo(Hypothesis other) {
      return Double.compare(this.rating, other.rating);
    }
  }

  /**
   * A {@link LanguageRecognizerExtended} with trivial implementation
   * of the {@link LanguageRecognizer#recognize(Expression)} method.
   *
   * @author Bartłomiej Etenkowski
   * @param <T> the input type
   */
  public static abstract class AbstractLanguageRecognizerExtended<T extends Expression>
  implements LanguageRecognizerExtended<T> {

    /**
     * <p>Tries to recognize the language of the given item. If the recognition fails,
     * the method returns {@code null}. Otherwise, it returns the ID of the language which
     * suits best to the item.</p>
     * <p>In this implementation, the {@link LanguageRecognizerExtended#recognize(Expression, int)}
     * is called and the language from the best hypothesis (or {@code null} if no hypothesis
     * has been found) is returned.</p>
     *
     * @param item the item to be recognized
     * @return the id of the recognized language or {@code null} if the language of the item
     *         cannot be recognized
     *
     * @throws UnsupportedInputTypeException if the particular input type is not supported
     */
    public String recognize(T item) {
      List<Hypothesis> rec = recognize(item, 1);
      if (rec.size() == 0) {
        return null;
      } else {
        return rec.get(0).language;
      }
    }

  }

}
